﻿using System;
using System.Linq;

using Prometheus.Binding.Core;
using Prometheus.TypeUtility;
#if NETFX_CORE
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup;
#else
using System.Windows.Controls;
using System.Windows.Markup;
#endif
namespace Prometheus.Binding.ReadOnly
{
    /// <summary>
    /// Binding for reading the value from datacontext
    /// </summary>
    [ContentProperty(
#if NETFX_CORE
        Name = 
#endif
"Converter")]
    public class proReadOnlyBinding : proConvertableBinding
    {
        public proReadOnlyBinding() : this(string.Empty, string.Empty)
        {
        }

        public proReadOnlyBinding(string elementPropertyPath) : this(elementPropertyPath, string.Empty)
        {
        }

        public proReadOnlyBinding(string elementPropertyPath, string dataContextPropertyPath)
            : base(elementPropertyPath, dataContextPropertyPath)
        {
        }

        /// <summary>
        /// Reads value form datacontext
        /// </summary>
        protected override void FromData()
        {
            object dataValue;
            try
            {
                dataValue = GetValueForTarget();
            }
            catch (SomethingInvalidException)
            {
                return; // if this happened - there is no any value to set on.
            }

            SetValueToTarget(dataValue);
        }

        protected override void ToData()
        {
            //we can not set value to datacontext
        }

        #region Value reading and writing

        /// <summary>
        /// Mode of syncing within the group
        /// Please, avoid of recursion
        /// </summary>
        public GroupUpdateMode UpdateGroupMode { get; set; }

        public virtual void SetValueToTarget(object dataValue)
        {
            if (uiElement != null)
            {
                try
                {
                    if ((UIElement is TextBox) && (ElementPropertyPath == "Text") && dataValue == null)
                        dataValue = string.Empty; //'cause text can't set null
                    dataValue = dataValue.CastOrConvert(TargetType);
                }
                catch (SomethingInvalidException)
                {
                    throw new InvalidOperationException(); //next string was at jSLCore, but I suggest it is useless
                    //throw new InvalidOperationException(proValidationResultType.Error, "Bad value");
                }

                if ((UIElement is TextBlock || UIElement is TextBox) && ElementPropertyPath == "Text" &&
                    dataValue == null)
                {
                    dataValue = string.Empty;
                }

                try
                {
                    uiElement.SetProperty(ElementPropertyPath, dataValue);
                }
                catch (InvalidOperationException)
                {
                }

                try
                {
                    SetTargetValueChanged(dataValue);
                }
                catch (Exception exception)
                {
                    throw new Exception("Exception occured during FromDataProcessed call. See innerException", exception);
                }
            }
        }

        /// <summary>
        /// Invoked when attempt to write target occured
        /// </summary>
        /// <param name="dataValue"></param>
        protected void SetTargetValueChanged(object dataValue)
        {
            InvokeTargetValueChanged(dataValue);

            if (ProFormBase != null)
                foreach (proBindingBase bindingBase in ProFormBase
                    .GetjBindingByParentIsThisAndBindingGroup(BindingGroup)
                    .Where(
                        b =>
                        (b is proReadOnlyBinding) &&
                        (((proReadOnlyBinding) b).UpdateGroupMode == GroupUpdateMode.UpdateTargetWhenGroupFromData ||
                         ((proReadOnlyBinding) b).UpdateGroupMode == GroupUpdateMode.Both))
                    .ToArray())
                {
                    if (bindingBase != this)
                        bindingBase.UpdateTarget();
                }
        }

        public event EventHandler<GenericEventArgs<object>> FromDataProcessed;

        public void InvokeTargetValueChanged(object val)
        {
            EventHandler<GenericEventArgs<object>> handler = FromDataProcessed;
            if (handler != null) handler(this, new GenericEventArgs<object>(val));
        }

        /// <summary>
        /// Methods returns the value, which should be setted to the target (After converting). It can throw SomethingInvalidException, if the value cannot be taken (if data context is null, for example)
        /// </summary>
        /// <returns>Methods returns the value, which should be setted to the target (After converting).</returns>
        public virtual object GetValueForTarget()
        {
            if (uiElement != null)
            {
                if (uiElement.DataContext == null)
                    throw new SomethingInvalidException(); //we do this to break the FromData process
                if (uiElement.DataContext.HasProperty(Path))
                {
                    object dataValue = uiElement.DataContext.GetProperty(Path);

                    dataValue = Convert(dataValue);
                    return dataValue;
                }
            }
            return null;
        }

        #endregion
    }
}